
#include <BEvent>
#include <BLayout>
#include "member_BLayout.h"
#include "member_BWidget.h"

using namespace BWE;

#define member					(*(member_BLayout*)_ptr)
#define member_allocate()		_ptr = new member_BLayout(this)
#define member_release()		delete (member_BLayout*)_ptr

BLayout::BLayout(BWidget* host)
{
	member_allocate();
	if (host)
		host->setLayout(this, Part_Center);
}
BLayout::BLayout(BWidget* host, PartFlag part)
{
	member_allocate();
	if (host)
		host->setLayout(this, part);
}
BLayout::BLayout(const BString& name, BWidget* host) : BObject(name)
{
	member_allocate();
	if (host)
		host->setLayout(this, Part_Center);
}
BLayout::BLayout(const BString& name, BWidget* host, PartFlag part) : BObject(name)
{
	member_allocate();
	if (host)
		host->setLayout(this, part);
}
BLayout::~BLayout()
{
	member_release();
}

bool BLayout::setEnabled(bool enabled)
{
	if (BObject::setEnabled(enabled))
	{
		member.dirty = true;
		if (member.parent)
		{
			member.parent->dirty(this);
			member.parent->dirty();
		}
		if (member.host)
		{
			member.host->adjust();
			member.host->fresh();
			bool enabled = widget_member(member.host)->state.enabled;
			widget_member(member.host)->freshEnabled(enabled);
		}
		emit(Signal_Dirty);
		return true;
	}
	return false;
}

void BLayout::fresh() const
{
	if (member.host)
	{
		member.host->fresh();
	}
}
void BLayout::dirty() const
{
	if (!member.dirty)
	{
		member.dirty = true;
		if (member.parent)
		{
			member.parent->dirty(this);
			member.parent->dirty();
		}
		else if (member.host)
		{
			member.host->adjust();
			member.host->fresh();
		}
		emit(Signal_Dirty);
	}
}

void BLayout::setHost(BWidget* host)
{
	if (member.item == host)
		return;
	if (member.host == host)
		return;
	if (member.host)
	{
		if (member.item.widget)
			member.host->removeChild(member.item.widget);
		if (member.item.layout)
			member.item.layout->setHost(0);
	}
	member.host = host;
	if (host)
	{
		if (member.item.widget)
			host->addChild(member.item.widget);
		if (member.item.layout)
			member.item.layout->setHost(host);
	}
	BLayout::dirty();
}
BWidget* BLayout::host() const
{
	return member.host;
}

BLayout* BLayout::parent()
{
	return member.parent;
}
const BLayout* BLayout::parent() const
{
	return member.parent;
}

void BLayout::setPart(PartFlag part)
{
	if (member.part != part)
	{
		member.part = part;
		this->dirty();
	}
}
PartFlag BLayout::part() const
{
	return member.part;
}

void BLayout::setSplitable(bool splitable)
{
	if (member.splitable != splitable)
	{
		member.splitable = splitable;
		this->dirty();
	}
}
bool BLayout::splitable() const
{
	return member.splitable;
}

void BLayout::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (member.host)
			member.host->fresh();
	}
}
bool BLayout::visible() const
{
	return member.visible;
}

void BLayout::setMargin(int left, int top, int right, int bottom)
{
	setMargin(BMargin(left, top, right, bottom));
}
void BLayout::setMargin(const BMargin& margin)
{
	if (member.margin != margin)
	{
		member.margin = margin;
		this->dirty();
	}
}
const BMargin& BLayout::margin() const
{
	return member.margin;
}

void BLayout::setMargin(Part part, int value)
{
	BMargin margin = member.margin;
	if (part & Part_Left)
		margin.left() = value;
	if (part & Part_Top)
		margin.top() = value;
	if (part & Part_Right)
		margin.right() = value;
	if (part & Part_Bottom)
		margin.bottom() = value;
	this->setMargin(margin);
}
int BLayout::margin(Part part) const
{
	switch (part)
	{
	case Part_Left:
		return member.margin.left();
	case Part_Top:
		return member.margin.top();
	case Part_Right:
		return member.margin.right();
	case Part_Bottom:
		return member.margin.bottom();
	default:
		return -1;
	}
}

void BLayout::setWidthPolicy(Policy widthPolicy)
{
	setSizePolicy(BSizePolicy(widthPolicy, member.sizePolicy.heightPolicy()));
}
Policy BLayout::widthPolicy() const
{
	return sizePolicy().widthPolicy();
}

void BLayout::setHeightPolicy(Policy heightPolicy)
{
	setSizePolicy(BSizePolicy(member.sizePolicy.widthPolicy(), heightPolicy));
}
Policy BLayout::heightPolicy() const
{
	return sizePolicy().heightPolicy();
}

void BLayout::setSizePolicy(Policy widthPolicy, Policy heightPolicy)
{
	setSizePolicy(BSizePolicy(widthPolicy, heightPolicy));
}
void BLayout::setSizePolicy(const BSizePolicy& sizePolicy)
{
	if (member.sizePolicy != sizePolicy)
	{
		member.sizePolicy = sizePolicy;
		this->dirty();
	}
}
BSizePolicy BLayout::sizePolicy() const
{
	BSizePolicy policy = member.sizePolicy;
	if (policy.widthPolicy() == Policy_None)
	{
		LayoutItem& item = member.item;
		if (item.dirty)
			item.flush();
		policy.widthPolicy() |= item.sizePolicy.widthPolicy();
	}
	if (policy.heightPolicy() == Policy_None)
	{
		LayoutItem& item = member.item;
		if (item.dirty)
			item.flush();
		policy.heightPolicy() |= item.sizePolicy.heightPolicy();
	}
	return policy;
}

void BLayout::setFixedSize(int fixedWidth, int fixedHeight)
{
	setFixedSize(BSize(fixedWidth, fixedHeight));
}
void BLayout::setFixedSize(const BSize& fixedSize)
{
	if (member.fixedSize != fixedSize)
	{
		member.fixedSize = fixedSize;
		this->dirty();
	}
}
BSize BLayout::fixedSize() const
{
	BSize fixedSize = layout_member(this)->fixedSize;
	if (fixedSize.width() == 0 || fixedSize.height() == 0)
	{
		if (member.item.dirty)
			member.item.flush();
		if (fixedSize.width() == 0)
			fixedSize.width() = member.item.fixedSize.width();
		if (fixedSize.height() == 0)
			fixedSize.height() = member.item.fixedSize.height();
	}
	return fixedSize;
}

void BLayout::setMinSize(int minWidth, int minHeight)
{
	setMinSize(BSize(minWidth, minHeight));
}
void BLayout::setMinSize(const BSize& minSize)
{
	if (member.minSize != minSize)
	{
		member.minSize = minSize;
		this->dirty();
	}
}
BSize BLayout::minSize() const
{
	return member.minSize;
}

void BLayout::setMaxSize(int maxWidth, int maxHeight)
{
	setMaxSize(BSize(maxWidth, maxHeight));
}
void BLayout::setMaxSize(const BSize& maxSize)
{
	if (member.maxSize != maxSize)
	{
		member.maxSize = maxSize;
		this->dirty();
	}
}
BSize BLayout::maxSize() const
{
	return member.maxSize;
}

BSize BLayout::preferSize() const
{
	BSizePolicy policy = this->sizePolicy();
	BSize fixedSize = this->fixedSize();
	BSize hintSize = this->sizeHint();
	BSize minSize = this->minSize();
	BSize maxSize = this->maxSize();
	BSize size = this->size();

	if (policy.widthPolicy() == Policy_Fixed)
		size.width() = fixedSize.width();
	else if (policy.widthPolicy() & Policy_Preferred)
		size.width() = hintSize.width();
	
	if (policy.heightPolicy() == Policy_Fixed)
		size.height() = fixedSize.height();
	else if (policy.heightPolicy() & Policy_Preferred)
		size.height() = hintSize.height();

	size.limit(minSize, maxSize);
	return size;
}

void BLayout::setRect(int x, int y, int width, int height)
{
	setRect(BRect(x, y, width, height));
}
void BLayout::setRect(const BPoint& pos, const BSize& size)
{
	setRect(BRect(pos, size));
}
void BLayout::setRect(const BRect& rect)
{
	if (member.rect != rect)
	{
		member.rect = rect;
		member.dirty = true;
	}
}
const BRect& BLayout::rect() const
{
	return member.rect;
}

bool BLayout::setAlign(Align align)
{
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}
Align BLayout::align() const
{
	return member.item.align;
}

bool BLayout::setWidget(BWidget* widget, Align align)
{
	if (member.item != widget)
	{
		member.item.clear();
		member.item.widget = widget;
		member.item.align = align;
		member.item.dirty = true;
		if (widget)
		{
			if (widget_member(widget)->upperLayout)
				widget_member(widget)->upperLayout->remove(widget);
			widget_member(widget)->upperLayout = this;
			if (member.host)
				member.host->addChild(widget);
		}
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		member.item.dirty = true;
		this->dirty();
	}
	return true;
}
bool BLayout::setLayout(BLayout* layout, Align align)
{
	if (member.item != layout)
	{
		member.item.clear();
		member.item.layout = layout;
		member.item.align = align;
		if (layout)
		{
			if (layout_member(layout)->parent)
				layout_member(layout)->parent->remove(layout);
			layout_member(layout)->parent = this;
			layout->setHost(member.host);
		}
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}
bool BLayout::setSpacer(BSpacer* spacer, Align align)
{
	if (member.item != spacer)
	{
		member.item.clear();
		member.item.spacer = spacer;
		member.item.align = align;
		this->dirty();
	}
	if (member.item.align != align)
	{
		member.item.align = align;
		this->dirty();
	}
	return true;
}

bool BLayout::dirty(const BObject* object) const
{
	if (member.item == object)
	{
		member.item.dirty = true;
		this->dirty();
		return true;
	}
	return false;
}
Align BLayout::align(const BObject* object) const
{
	if (member.item == object)
	{
		return member.item.align;
	}
	return Align_None;
}
const BRect& BLayout::area(const BObject* object) const
{
	if (member.item == object)
	{
		return member.item.rect;
	}
	return member.rect;
}

BObject* BLayout::find(const BString& name) const
{
	if (BWidget* widget = member.item.widget)
	{
		if (widget->name() == name)
			return widget;
	}
	if (BSpacer* spacer = member.item.spacer)
	{
		if (spacer->name() == name)
			return spacer;
	}
	if (BSpring* spring = member.item.spring)
	{
		if (spring->name() == name)
			return spring;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->name() == name)
			return layout;
		BObject* obj = layout->find(name);
		if (obj)
			return obj;
	}
	return 0;
}
BWidget* BLayout::findWidget(const BString& name) const
{
	if (BWidget* widget = member.item.widget)
	{
		if (widget->name() == name)
			return widget;
	}
	if (BLayout* layout = member.item.layout)
	{
		BWidget* widget = layout->findWidget(name);
		if (widget)
			return widget;
	}
	return 0;
}
BLayout* BLayout::findLayout(const BString& name) const
{
	if (BLayout* layout = member.item.layout)
	{
		if (layout->name() == name)
			return layout;
		BLayout* lay = layout->findLayout(name);
		if (lay)
			return lay;
	}
	return 0;
}
BSpacer* BLayout::findSpacer(const BString& name) const
{
	if (BSpacer* spacer = member.item.spacer)
	{
		if (spacer->name() == name)
			return spacer;
	}
	if (BLayout* layout = member.item.layout)
	{
		BSpacer* spacer = layout->findSpacer(name);
		if (spacer)
			return spacer;
	}
	return 0;
}
BSpring* BLayout::findSpring(const BString& name) const
{
	if (BSpring* spring = member.item.spring)
	{
		if (spring->name() == name)
			return spring;
	}
	if (BLayout* layout = member.item.layout)
	{
		BSpring* spring = layout->findSpring(name);
		if (spring)
			return spring;
	}
	return 0;
}

bool BLayout::contain(const BObject* object) const
{
	if (object)
	{
		if (const BWidget* widget = dynamic_cast<const BWidget*>(object))
			return contain(widget);
		if (const BLayout* layout = dynamic_cast<const BLayout*>(object))
			return contain(layout);
		if (const BSpring* spring = dynamic_cast<const BSpring*>(object))
			return contain(spring);
		if (const BSpacer* spacer = dynamic_cast<const BSpacer*>(object))
			return contain(spacer);
	}
	return false;
}
bool BLayout::contain(const BWidget* widget) const
{
	if (member.item.widget)
	{
		if (member.item.widget == widget)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(widget))
			return true;
	}
	return false;
}
bool BLayout::contain(const BLayout* layout) const
{
	if (member.item.layout)
	{
		if (member.item.layout == layout)
			return true;
		if (member.item.layout->contain(layout))
			return true;
	}
	return false;
}
bool BLayout::contain(const BSpacer* spacer) const
{
	if (member.item.spacer)
	{
		if (member.item.spacer == spacer)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(spacer))
			return true;
	}
	return false;
}
bool BLayout::contain(const BSpring* spring) const
{
	if (member.item.spring)
	{
		if (member.item.spring == spring)
			return true;
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->contain(spring))
			return true;
	}
	return false;
}

bool BLayout::remove(BObject* object)
{
	if (object)
	{
		if (BWidget* widget = dynamic_cast<BWidget*>(object))
			return remove(widget);
		if (BLayout* layout = dynamic_cast<BLayout*>(object))
			return remove(layout);
		if (BSpring* spring = dynamic_cast<BSpring*>(object))
			return remove(spring);
		if (BSpacer* spacer = dynamic_cast<BSpacer*>(object))
			return remove(spacer);
	}
	return false;
}
bool BLayout::remove(BWidget* widget)
{
	if (member.item.widget)
	{
		if (member.item.widget == widget)
		{
			member.item.widget = 0;
			this->dirty();
			if (this->host())
				this->host()->removeChild(widget);
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(widget))
			return true;
	}
	return false;
}
bool BLayout::remove(BLayout* layout)
{
	if (member.item.layout)
	{
		if (member.item.layout == layout)
		{
			if (this->host())
				layout->setHost(0);
			member.item.layout = 0;
			this->dirty();
			return true;
		}
		if (member.item.layout->remove(layout))
			return true;
	}
	return false;
}
bool BLayout::remove(BSpacer* spacer)
{
	if (member.item.spacer)
	{
		if (member.item.spacer == spacer)
		{
			member.item.spacer = 0;
			this->dirty();
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(spacer))
			return true;
	}
	return false;
}
bool BLayout::remove(BSpring* spring)
{
	if (member.item.spring)
	{
		if (member.item.spring == spring)
		{
			member.item.spring = 0;
			this->dirty();
			return true;
		}
	}
	if (BLayout* layout = member.item.layout)
	{
		if (layout->remove(spring))
			return true;
	}
	return false;
}
void BLayout::clear()
{
	if (member.item.empty())
		return;
	if (BWidget* widget = member.item.widget)
	{
		remove(widget);
	}
	if (BLayout* layout = member.item.layout)
	{
		remove(layout);
	}
	if (BSpacer* spacer = member.item.spacer)
	{
		remove(spacer);
	}
	if (BSpring* spring = member.item.spring)
	{
		remove(spring);
	}
}

int BLayout::count() const
{
	return 1;
}
BObject* BLayout::object(int pos) const
{
	if (pos == 0)
	{
		LayoutItem& item = member.item;
		if (item.widget)
			return item.widget;
		if (item.layout)
			return item.layout;
		if (item.spacer)
			return item.spacer;
		if (item.spring)
			return item.spring;
	}
	return 0;
}
BWidget* BLayout::widget(int pos) const
{
	if (pos == 0)
	{
		member.item.widget;
	}
	return 0;
}
BLayout* BLayout::layout(int pos) const
{
	if (pos == 0)
	{
		member.item.layout;
	}
	return 0;
}
BSpacer* BLayout::spacer(int pos) const
{
	if (pos == 0)
	{
		member.item.spacer;
	}
	return 0;
}
BSpring* BLayout::spring(int pos) const
{
	if (pos == 0)
	{
		member.item.spring;
	}
	return 0;
}

BSize BLayout::sizeHint() const
{
	if (member.item.dirty)
		member.item.flush();
	return member.item.sizeHint + this->margin().size();
}
BSize BLayout::size() const
{
	return member.rect.size();
}
bool BLayout::adjust() const
{
	if (!layout_member(this)->dirty)
		return false;

	if (this->rect().empty() || member.item.empty())
	{
		layout_member(this)->dirty = false;
		return true;
	}

	const BRect& rect = this->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	const BMargin& margin = this->margin();
	int posX = x + margin.left();
	int posY = y + margin.top();
	int width = rect.width() - margin.width();
	int height = rect.height() - margin.height();

	if (member.item.prepare(height, width, height))
	{
		member.item.adjust(posX, posY, width, height);
	}
	layout_member(this)->dirty = false;
	return true;
}

bool BLayout::event(const BEvent& event)
{
	BLayout* layout = member.item.layout;
	if (layout && layout->enabled())
	{
		if (layout_member(layout)->event(event))
			return true;
	}
	if (event.type() == Event_Style)
	{
		this->styleEvent(event);
		return false;
	}
	if (member.splitable)
	{
		switch (event.type())
		{
		case Event_Fresh:
			this->freshEvent(event);
			break;
		case Event_Paint:
			this->paintEvent(event);
			break;

		case Event_MouseEnter:
			return this->mouseEnterEvent((const BMouseEvent&)event);
		case Event_MouseLeave:
			return this->mouseLeaveEvent((const BMouseEvent&)event);
		case Event_MouseMove:
			return this->mouseMoveEvent((const BMouseEvent&)event);
		case Event_MousePress:
			return this->mousePressEvent((const BMouseEvent&)event);
		case Event_MouseRelease:
			return this->mouseReleaseEvent((const BMouseEvent&)event);
		default:
			return false;
		}
	}
	return false;
}

void BLayout::styleEvent(const BEvent& event)
{
	const BStyle* style = event.value();

	bool changed = false;

	const BValue& marginLeftValue = style->value(Value_Margin_Left);
	if (marginLeftValue.valid())
	{
		member.margin.left() = marginLeftValue;
		changed = true;
	}

	const BValue& marginTopValue = style->value(Value_Margin_Top);
	if (marginTopValue.valid())
	{
		member.margin.top() = marginTopValue;
		changed = true;
	}

	const BValue& marginRightValue = style->value(Value_Margin_Right);
	if (marginRightValue.valid())
	{
		member.margin.right() = marginRightValue;
		changed = true;
	}

	const BValue& marginBottomValue = style->value(Value_Margin_Bottom);
	if (marginBottomValue.valid())
	{
		member.margin.bottom() = marginBottomValue;
		changed = true;
	}

	if (changed)
		member.dirty = true;
}
void BLayout::freshEvent(const BEvent& event)
{

}
void BLayout::paintEvent(const BEvent& event)
{

}

bool BLayout::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool BLayout::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	return false;
}

bool BLayout::mousePressEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool BLayout::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
bool BLayout::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	return false;
}
